using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using data.contracts;

namespace modelbuilder.parts
{
    public class BoardBuilder : IBoardBuilder
    {
        public void In_Process(Tuple<IEnumerable<XDocument>, string> message) {
            Trace.TraceInformation("BoardBuilder.In_Process xml count = {0}", message.Item1.Count());
            Out_Result(BuildBoards(message.Item1, message.Item2));
        }

        public event Action<IEnumerable<Board>> Out_Result;

        private static IEnumerable<Board> BuildBoards(IEnumerable<XDocument> xmls, string @namespace) {
            foreach (var document in xmls) {
                var boardElement = document.Elements("board").First();
                var board = new Board(BoardName(boardElement), @namespace);
                AddInterface(board, boardElement);
                AddWires(board, boardElement);
                AddUsings(board, boardElement);
                AddExternals(board, boardElement);
                AddDependencies(board, boardElement);
                yield return board;
            }
        }

        private static string BoardName(XElement boardElement) {
            return boardElement.Attributes("name").First().Value;
        }

        private static void AddInterface(Board board, XElement boardElement) {
            var @interface = boardElement.Attributes("implements").FirstOrDefault();
            if (@interface == null) {
                return;
            }
            board.SetInterface(@interface.Value);
        }

        private static void AddWires(Board board, XElement boardElement) {
            foreach (var wireElement in boardElement.Elements("wire")) {
                var wire = new Wire(
                    ReplaceCurlyBraces(wireElement.Attribute("from").Value),
                    ReplaceCurlyBraces(wireElement.Attribute("type").Value),
                    ReplaceCurlyBraces(wireElement.Attribute("to").Value));
                board.Add(wire);
            }
        }

        private static string ReplaceCurlyBraces(string value) {
            return value
                .Replace('{', '<')
                .Replace('}', '>');
        }

        private static void AddUsings(Board board, XElement boardElement) {
            AddAttributeOfElement(boardElement, "using", "namespace", board.AddUsing);
        }

        private static void AddExternals(Board board, XElement boardElement) {
            AddAttributeOfElement(boardElement, "external", "name", board.AddExternal);
        }

        private static void AddDependencies(Board board, XElement boardElement) {
            foreach (var element in boardElement.Elements("dependency")) {
                var from = ReplaceCurlyBraces(element.Attribute("from").Value);
                var to = ReplaceCurlyBraces(element.Attribute("to").Value);
                board.AddDependency(new Dependency{ Dependent = from, Type = to});
            }
        }

        private static void AddAttributeOfElement(XElement rootElement, string elementName, string attributeName, Action<string> addFunction) {
            foreach (var element in rootElement.Elements(elementName)) {
                var value = element.Attribute(attributeName).Value;
                addFunction(value);
            }
        }
    }
}